package com.assistant.tasks.service.impl;

import com.assistant.common.Constants;
import com.assistant.common.PageResult;
import com.assistant.common.exception.TaskNotExistsException;
import com.assistant.common.exception.file.InvalidExtensionException;
import com.assistant.common.utils.spring.SpringUtils;
import com.assistant.common.utils.uuid.Seq;
import com.assistant.tasks.mapper.TasksMapper;
import com.assistant.tasks.pojo.Task;
import com.assistant.tasks.pojo.TaskDTO;
import com.assistant.tasks.service.TasksService;
import com.assistant.tasks.thread.AnalysisThread;
import com.assistant.tasks.thread.PredictThread;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;

/**
 * @Classname TasksServiceImpl
 * @Description TODO
 * @Date 2025/3/4 17:58
 * @Created by xxx
 */
@Service
@Slf4j
public class TasksServiceImpl implements TasksService {
    @Autowired
    private TasksMapper tasksMapper;
    @Value("${file.path0}")
    private String fileDir0;
    @Value("${file.path1}")
    private String fileDir1;

    private ThreadPoolTaskExecutor executor = SpringUtils.getBean("threadPoolTaskExecutor");

    @Override
    public PageResult getTasks(TaskDTO taskDTO) {
        PageHelper.startPage(taskDTO.getPage(), taskDTO.getPageSize());
        Page<Task> pageQuery = tasksMapper.pageQuery(taskDTO);
        PageResult pageResult = new PageResult();
        pageResult.setTotal(pageQuery.getTotal());
        pageResult.setRecords(pageQuery.getResult());
        return pageResult;
    }

    @Async
    @Override
    public void createTask(TaskDTO taskDTO) throws InvalidExtensionException {
        //文件后缀不是zip，提示文件格式错误。
        if (taskDTO.getFile() != null) {
            String fileName = taskDTO.getFile().getOriginalFilename();
            String extension = null;
            if (fileName != null) {
                extension = fileName.substring(fileName.lastIndexOf(".") + 1);
            }
            if (extension != null && !extension.equals("zip")) {
                throw new InvalidExtensionException(new String[]{Constants.ZIP}, extension, fileName);
            }
        }
        String fileId = Seq.generate();
        if (taskDTO.getFileId() == null) {
            taskDTO.setFileId(fileId);
        }
        taskDTO.setState(Constants.STATE_PENDING);
        taskDTO.setCreateTime(LocalDateTime.now());
        taskDTO.setStateTime(LocalDateTime.now());
        //任务类型为统计、不包含pcdn, 直接请求报表生成接口，保存报表文件。
        if (taskDTO.getCategory() == Constants.CATEGORY_ANALYSIS && taskDTO.getContainsPcdn() == 0) {
            tasksMapper.insert(taskDTO);
            //new Thread(new AnalysisThread(taskDTO, fileDir0, tasksMapper)).start();
            executor.execute(new AnalysisThread(taskDTO, fileDir0, tasksMapper));
        }
        //1. 检测是否存在同月份的预测任务，如果不存在则，提示先进行预测任务。如果存在，则 直接请求报表生成接口
        //2. 保存生成的报表文件。
        else if (taskDTO.getCategory() == Constants.CATEGORY_ANALYSIS && taskDTO.getContainsPcdn() == 1) {
            TaskDTO predictTaskDTO = new TaskDTO();
            predictTaskDTO.setCategory(Constants.CATEGORY_PREDICT);
            predictTaskDTO.setDate(taskDTO.getDate());
            predictTaskDTO.setState(Constants.STATE_SUCCESS);
            Integer count = tasksMapper.countTask(predictTaskDTO);
            if (count == 0) {
                log.info("请先进行预测任务");
                throw new TaskNotExistsException("请先进行预测任务");
            } else {
                tasksMapper.insert(taskDTO);
                //new Thread(new AnalysisThread(taskDTO, fileDir0, tasksMapper)).start();
                executor.execute(new AnalysisThread(taskDTO, fileDir1, tasksMapper));
            }
        }
        //1. 将压缩包解压，读取文件，请求模型，并将结果写入文件。
        //2. 将写入后的文件,压缩成zip,并根据月份命名,请求文件上传接口。
        //3. 检测是否存在同月份的统计任务，如果存在，则生成子任务，类型为统计, 传递date参数。
        //4. 将子任务的生成的文件覆盖原有的同月份统计任务。
        else if (taskDTO.getCategory() == Constants.CATEGORY_PREDICT) {
            tasksMapper.insert(taskDTO);
            //new Thread(new PredictThread(taskDTO, fileDir1, tasksMapper, this)).start();
            executor.execute(new PredictThread(taskDTO, fileDir1, tasksMapper, this));
        }
    }

    @Override
    public String getResult(Integer id, HttpServletResponse response) throws IOException {
        // 1. 校验任务是否存在
        TaskDTO task = tasksMapper.getTaskById(id);
        if (task == null) {
            log.warn("Task not found, id: {}", id);
            return "Task not found";
        }
        // 2. 校验任务状态
        if (Constants.STATE_SUCCESS != task.getState()) {
            log.warn("Task not in success state, id: {}, state: {}", id, task.getState());
            return task.getMsg();
        }
        // 3. 校验文件存在性
        Path filePath = Paths.get(task.getFilePath());
        if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) {
            log.error("File not found, path: {}", task.getFilePath());
            return "File not found";
        }
        // 4. 设置响应头
        String encodedFileName = URLEncoder.encode(filePath.getFileName().toString(), "UTF-8")
                .replaceAll("\\+", "%20");
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + encodedFileName + "\";" +
                        "filename*=UTF-8''" + encodedFileName);
        response.setContentLengthLong(Files.size(filePath));
        // 5. 流式传输文件
        try (InputStream is = Files.newInputStream(filePath);
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024 * 16];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
        } catch (IOException e) {
            log.error("File transfer interrupted, task: {}", id, e);
            return "File transfer interrupted";
        }
        return null;
    }


}

